#ifndef _PERSEUS_H__
#define _PERSEUS_H__

/* PCC parameters limits */
/* [X_MIN; X_MAX + X_MIN] */

#define KMIN_GEN 1
#define KMAX_GEN 5

#define NMAX_GEN 5
#define NMIN_GEN 6

#define MIN_CONT 10
#define MAX_CONT 20 

#define MIN_MATWIDTH 6 
#define MAX_MATWIDTH 16

/* Noise generator feedback polynomial 1 */
#define POLY1 0x47E07L
#define MASK1 0x7FFFFL
#define LR1 19

/* Noise generator feedback polynomial 2 */
#define POLY2 0x1772AFL
#define MASK2 0x7FFFFFL
#define LR2 23

/* Noise generator feedback polynomial 3 */
#define POLY3 0x1C95269L
#define MASK3 0x1FFFFFFFL
#define LR3 29

/* Noise generator feedback polynomial 4 */
#define POLY4 0x43E98841L
#define MASK4 0x7FFFFFFFL
#define LR4 31

/* alea(): a random double in [0, 1]      */
double alea(void);

/* Generic type for a Punctured Convolutional Code */
typedef struct 
 {
  unsigned int mN;         /* Number of output bits     */ /* secret */ 
  unsigned int mK;         /* Number of input  bits     */ /* secret */ 
  unsigned int mM;         /* Encoder memory size       */ /* secret */ 
   /* mPoly is an array of mK pointers to arrays of mN unsigned longs */
  unsigned long * * mPoly; /* Encoder polynomials       */ /* secret */ 
  unsigned int mMatWidth;  /* Puncturing matrix width   */ /* secret */ 
   /* mMatrix is an array of (mN * mMatWidth) unsigned chars */
  unsigned char * mMatrix; /* Encoder puncturing matrix */ /* secret */ 
  unsigned int mMatDepth;  /* Puncturing matrix weight  */ /* secret */ 
 } PUNCT_CONC_CODE;

/* Generic type for a noise generator secret key   */
typedef struct
 {
  unsigned long int INIT1;                                         /* secret */ 
  unsigned long int INIT2;                                         /* secret */ 
  unsigned long int INIT3;                                         /* secret */ 
  unsigned long int INIT4;                                         /* secret */ 
  unsigned char *Bf ;      /* Combining Boolean function       */  /* secret */ 
 } INIT_NOISE_GEN;

/* Generic type for a noise generator              */
typedef struct
 {
  unsigned long int Reg1;  /* Linear Feedback Shift Register 1 */
  unsigned long int Reg2;  /* Linear Feedback Shift Register 2 */
  unsigned long int Reg3;  /* Linear Feedback Shift Register 3 */
  unsigned long int Reg4;  /* Linear Feedback Shift Register 4 */
  unsigned int L1;         /* Length of LFSR 1                 */
  unsigned int L2;         /* Length of LFSR 2                 */
  unsigned int L3;         /* Length of LFSR 3                 */
  unsigned int L4;         /* Length of LFSR 4                 */
 } NOISE_GEN;

/* Generic type for location in Viterbi lattice    */
typedef struct position_t
 {
  unsigned int input;
  unsigned long* memory;
  struct position_t * previous;
 } position;

/* Generic type for Viterbi lattice node           */
typedef struct noded_t
 {
  position ** Block;
  char metric;
  unsigned long positionNumber;
 } noded;

/* Generic type for a Viterbi lattice              */
typedef struct viterbi_t 
 {
  noded *** treillis;
  unsigned long width;
  unsigned int height;
  PUNCT_CONC_CODE * aCode;
  unsigned char * data;
  unsigned long dataLength;
 } viterbi;


typedef struct libperseus_hl
 {
   PUNCT_CONC_CODE * Pcc;
   NOISE_GEN * NGen;
   INIT_NOISE_GEN * aKey;
 } libperseus_hl_t;

/***************************************************/
/* Prototypes of the libperseus functions          */
/***************************************************/

/* Core functions                                  */
PUNCT_CONC_CODE * generateCode(void); 
INIT_NOISE_GEN *generateKey(void);

int Gen_Noise_Generator(NOISE_GEN *, INIT_NOISE_GEN *);
int Gen_Noise(unsigned char *, NOISE_GEN *, unsigned long int, INIT_NOISE_GEN *);
unsigned int pcc_code(PUNCT_CONC_CODE *, unsigned char *, unsigned long, unsigned char **, unsigned long *, NOISE_GEN *, INIT_NOISE_GEN *);
unsigned char pcc_puncture(PUNCT_CONC_CODE *, unsigned char *, unsigned long, unsigned char **, unsigned long *);
unsigned char pcc_decode(PUNCT_CONC_CODE *, NOISE_GEN *, INIT_NOISE_GEN *, unsigned char *, unsigned long, unsigned char **, unsigned long *);
unsigned char pcc_unpuncture(PUNCT_CONC_CODE *,unsigned char *, unsigned long, unsigned char **, unsigned long *);
void delete_code(PUNCT_CONC_CODE *);


/* Miscellanous functions for encoder handling     */
unsigned int pcc_getN(PUNCT_CONC_CODE *);
unsigned int pcc_getK(PUNCT_CONC_CODE *);
unsigned int pcc_getM(PUNCT_CONC_CODE *);
unsigned long * * pcc_getPoly(PUNCT_CONC_CODE *);
unsigned int pcc_getMatWidth(PUNCT_CONC_CODE *);
unsigned char * pcc_getMatrix(PUNCT_CONC_CODE *);
void compute(unsigned int, unsigned int, unsigned long *, unsigned char, unsigned long, unsigned int *);
unsigned char motpar(unsigned long int);

/* Functions for the Viterbi decoding part and      */
/* and Viterbi lattice handling                     */
viterbi * viterbi_init(PUNCT_CONC_CODE *,unsigned char *, unsigned long);
unsigned char viterbi_decode(viterbi *,unsigned char **, unsigned long *);
unsigned char viterbi_addPosition(viterbi *, unsigned long, unsigned int, char, unsigned int, unsigned long *, unsigned char, position *);
void viterbi_setMetric(viterbi *, unsigned long, unsigned int, char);
char viterbi_getMetric(viterbi *, unsigned long, unsigned int);
char viterbi_isEmpty(viterbi *, unsigned long, unsigned int);
unsigned int viterbi_NumberOfPosition(viterbi *, unsigned long, unsigned int);
unsigned long * viterbi_getMemory(viterbi *, unsigned long, unsigned int, unsigned int);
position * viterbi_getAddressPosition(viterbi *, unsigned long, unsigned int, unsigned int);
unsigned char distance(unsigned char *, unsigned int, unsigned char, unsigned long);
unsigned char viterbi_backward(viterbi *, unsigned char **, unsigned long *);
void viterbi_delete(viterbi *);

/* Lattice nodes handling functions                 */
noded * noded_init(char, unsigned long, position **);
unsigned char noded_addPosition(noded *, char,unsigned int,unsigned long *, unsigned char, position *);
unsigned char noded_replaceBlock(noded *, char,unsigned int,unsigned long *, unsigned char, position *);
char noded_isEmpty(noded*);
void noded_delete(noded *);
void noded_setMetric(noded *, char);
char noded_getMetric(noded *);
unsigned long noded_getPositionNumber(noded *);
unsigned long * noded_getMemory(noded *,unsigned long);
position * noded_getAddressPosition(noded *,unsigned long);

/* Lattice nodes location handling functions        */
position * position_new(unsigned int, position *);
unsigned char position_copyMemory(position *p,unsigned long *, unsigned char);
void position_delete(position *);
void position_setPrevious(position *, position *);
unsigned long * position_getMemory(position *);
position * position_getPrevious(position *);
unsigned int position_getInput(position *);


/* Functions for string and data handling          */
unsigned char char_to_bin(unsigned char *, unsigned long, unsigned char, unsigned char, unsigned char **, unsigned long *);
unsigned char ** bin_to_charHex(unsigned char *, unsigned long, unsigned char **, unsigned long *);
unsigned char hex_to_Binchar(unsigned char *, unsigned long, unsigned char **, unsigned long *);
unsigned char * int_to_char(unsigned long *, unsigned long, unsigned int, unsigned int, unsigned long *);

/* High level functions */
int perseus_init(libperseus_hl_t *);
unsigned char *perseus_code(libperseus_hl_t *, unsigned char *, unsigned long int, unsigned long int *);
unsigned char *perseus_decode(libperseus_hl_t *, unsigned char *, unsigned long int, unsigned long int *);
void perseus_destroy(libperseus_hl_t *);

#endif /*_PERSEUS_H__*/
